<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>哲学家就餐问题</title>
  <style>
    :root {
      --primary-color: #2c3e50;
      --secondary-color: #3498db;
      --success-color: #27ae60;
      --warning-color: #f1c40f;
      --danger-color: #e74c3c;
      --history-bg: #f8f9fa;
    }

    body {
      font-family: 'Segoe UI', Arial, sans-serif;
      background: #ecf0f1;
      display: flex;
      flex-direction: column;
      align-items: center;
      min-height: 100vh;
      margin: 0;
      padding: 20px;
    }

    .main-container {
      display: flex;
      gap: 30px;
      max-width: 1200px;
      width: 100%;
    }

    .simulation-area {
      flex: 2;
    }

    .history-area {
      flex: 1;
      max-height: 600px;
      overflow-y: auto;
    }

    .container {
      position: relative;
      width: 600px;
      height: 600px;
      margin: 0 auto;
    }

    .philosopher {
      position: absolute;
      width: 80px;
      height: 80px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: all 0.3s ease;
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
      background: white;
      cursor: pointer;
    }

    .philosopher[data-state="thinking"] {
      background: var(--primary-color);
      color: white;
    }

    .philosopher[data-state="eating"] {
      background: var(--success-color);
      transform: scale(1.1);
    }

    .philosopher[data-state="hungry"] {
      background: var(--warning-color);
      animation: pulse 1.5s infinite;
    }

    .fork {
      position: absolute;
      width: 30px;
      height: 30px;
      background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 4v2h3v12h2V6h3V4H3m12 0v12h2V4h-2Z"/></svg>');
      background-size: cover;
      transition: transform 0.3s ease;
    }

    .fork[data-in-use="true"] {
      filter: invert(25%) sepia(90%) saturate(2000%) hue-rotate(-50deg);
      transform: rotate(45deg);
    }

    @keyframes pulse {
      0% { transform: scale(1); }
      50% { transform: scale(1.05); }
      100% { transform: scale(1); }
    }

    .stats {
      position: fixed;
      top: 20px;
      right: 20px;
      background: white;
      padding: 15px;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      z-index: 100;
    }

    .history-panel {
      background: var(--history-bg);
      border-radius: 8px;
      padding: 15px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }

    .history-title {
      margin-top: 0;
      border-bottom: 1px solid #ddd;
      padding-bottom: 10px;
    }

    .history-list {
      list-style: none;
      padding: 0;
      margin: 0;
    }

    .history-item {
      padding: 8px 12px;
      margin-bottom: 6px;
      border-radius: 4px;
      background: white;
      box-shadow: 0 1px 2px rgba(0,0,0,0.1);
      display: flex;
      justify-content: space-between;
    }

    .history-item[data-state="thinking"] {
      border-left: 4px solid var(--primary-color);
    }

    .history-item[data-state="eating"] {
      border-left: 4px solid var(--success-color);
    }

    .history-item[data-state="hungry"] {
      border-left: 4px solid var(--warning-color);
    }

    .history-time {
      color: #666;
      font-size: 0.8em;
    }

    .philosopher-selector {
      margin-bottom: 15px;
    }

    .philosopher-selector select {
      padding: 8px;
      border-radius: 4px;
      border: 1px solid #ddd;
    }
  </style>
</head>
<body>
<h1>哲学家就餐问题</h1>

<div class="main-container">
  <div class="simulation-area">
    <div class="container" id="container"></div>
  </div>

  <div class="history-area">
    <div class="history-panel">
      <h3 class="history-title">状态历史记录</h3>
      <div class="philosopher-selector">
        <select id="philosopherFilter">
          <option value="all">所有哲学家</option>
          <!-- 动态生成哲学家选项 -->
        </select>
      </div>
      <ul class="history-list" id="historyList"></ul>
    </div>
  </div>
</div>

<div class="stats" id="stats"></div>

<script>
  class DiningPhilosophers {
    constructor(num = 5) {
      this.NUM_PHILOSOPHERS = num;
      this.philosophers = [];
      this.forks = Array(this.NUM_PHILOSOPHERS).fill(false);
      this.startTime = Date.now();
      this.history = [];
      this.initDOM();
      this.startSimulation();
    }

    initDOM() {
      const container = document.getElementById('container');
      const angle = (2 * Math.PI) / this.NUM_PHILOSOPHERS;
      const radius = 250;

      // 初始化哲学家选择器
      const filter = document.getElementById('philosopherFilter');
      for (let i = 1; i <= this.NUM_PHILOSOPHERS; i++) {
        const option = document.createElement('option');
        option.value = i-1;
        option.textContent = `哲学家 ${i}`;
        filter.appendChild(option);
      }
      filter.addEventListener('change', () => this.updateHistoryDisplay());

      // 创建哲学家和叉子
      for (let i = 0; i < this.NUM_PHILOSOPHERS; i++) {
        const x = radius + radius * Math.cos(i * angle - Math.PI/2);
        const y = radius + radius * Math.sin(i * angle - Math.PI/2);

        // 创建哲学家
        const phil = document.createElement('div');
        phil.className = 'philosopher';
        phil.id = `philosopher${i}`;
        phil.textContent = i + 1;
        phil.style.left = `${x}px`;
        phil.style.top = `${y}px`;
        phil.addEventListener('click', () => {
          this.filterHistoryByPhilosopher(i);
        });
        container.appendChild(phil);

        // 创建叉子
        const fork = document.createElement('div');
        fork.className = 'fork';
        fork.id = `fork${i}`;
        const forkAngle = i * angle + angle/2 - Math.PI/2;
        fork.style.left = `${radius + (radius-40) * Math.cos(forkAngle)}px`;
        fork.style.top = `${radius + (radius-40) * Math.sin(forkAngle)}px`;
        container.appendChild(fork);
      }
    }

    startSimulation() {
      for (let i = 0; i < this.NUM_PHILOSOPHERS; i++) {
        this.philosophers.push(
                this.philosopherLifecycle(i)
        );
      }
      this.updateStats();
    }

    async philosopherLifecycle(id) {
      while (true) {
        await this.think(id);
        await this.acquireForks(id);
        await this.eat(id);
        this.releaseForks(id);
      }
    }

    async think(id) {
      await this.updateState(id, 'thinking');
      await this.delay(1000 + Math.random() * 2000);
    }

    async acquireForks(id) {
      await this.updateState(id, 'hungry');
      const [left, right] = this.getForkIndexes(id);

      if (id % 2 === 0) {
        await this.acquireFork(left);
        await this.acquireFork(right);
      } else {
        await this.acquireFork(right);
        await this.acquireFork(left);
      }
    }

    async acquireFork(index) {
      while (this.forks[index]) {
        await this.delay(100);
      }
      this.forks[index] = true;
      document.getElementById(`fork${index}`).dataset.inUse = true;
    }

    async eat(id) {
      await this.updateState(id, 'eating');
      await this.delay(500 + Math.random() * 1500);
    }

    releaseForks(id) {
      const [left, right] = this.getForkIndexes(id);
      this.forks[left] = false;
      this.forks[right] = false;
      document.getElementById(`fork${left}`).dataset.inUse = false;
      document.getElementById(`fork${right}`).dataset.inUse = false;
    }

    getForkIndexes(id) {
      return [id, (id + 1) % this.NUM_PHILOSOPHERS];
    }

    async updateState(id, state) {
      const element = document.getElementById(`philosopher${id}`);
      element.dataset.state = state;
      element.title = `哲学家 ${id+1}：${this.getStateText(state)}`;

      // 记录状态变化历史
      const timestamp = new Date();
      const historyEntry = {
        id,
        name: `哲学家 ${id+1}`,
        state,
        timestamp,
        timeElapsed: Math.floor((Date.now() - this.startTime) / 1000)
      };
      this.history.push(historyEntry);
      this.updateHistoryDisplay();
    }

    getStateText(state) {
      return {
        thinking: '思考中 🧠',
        eating: '就餐中 🍝',
        hungry: '等待叉子 🕒'
      }[state];
    }

    updateStats() {
      const statsElement = document.getElementById('stats');
      setInterval(() => {
        const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
        const eatingCount = [...document.querySelectorAll('.philosopher')]
                .filter(el => el.dataset.state === 'eating').length;

        statsElement.innerHTML = `
          <h3>统计数据</h3>
          <div>运行时间: ${elapsed}s</div>
          <div>当前就餐人数: ${eatingCount}</div>
          <div>空闲叉子: ${this.forks.filter(f => !f).length}</div>
          <div>状态变更次数: ${this.history.length}</div>
        `;
      }, 1000);
    }

    updateHistoryDisplay() {
      const list = document.getElementById('historyList');
      const filter = document.getElementById('philosopherFilter');
      const selectedPhil = filter.value;

      // 清空现有列表
      list.innerHTML = '';

      // 筛选并显示最新的20条记录
      const filteredHistory = selectedPhil === 'all'
              ? this.history.slice(-20)
              : this.history.filter(entry => entry.id == selectedPhil).slice(-20);

      filteredHistory.reverse().forEach(entry => {
        const item = document.createElement('li');
        item.className = 'history-item';
        item.dataset.state = entry.state;
        item.innerHTML = `
          <span>${entry.name} - ${this.getStateText(entry.state)}</span>
          <span class="history-time">${entry.timeElapsed}s</span>
        `;
        list.appendChild(item);
      });
    }

    filterHistoryByPhilosopher(id) {
      document.getElementById('philosopherFilter').value = id;
      this.updateHistoryDisplay();
    }

    delay(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }
  }

  // 启动模拟
  new DiningPhilosophers(5);
</script>
</body>
</html>